#include "spimcore.h"


/* ALU */
/* 10 Points */
void ALU(unsigned A,unsigned B,char ALUControl,unsigned *ALUresult,char *Zero)
{
	switch(ALUControl){
		case 0:
			//Z = A + B
			*ALUresult = A + B;
			break;
		case 1:
			//Z = A - B
			*ALUresult = A - B;
			if(*ALUresult == 0) *Zero = 1;
			else *Zero = 0;
			break;
		case 2:
			//if A < B Z = 1, else Z = 0
			if((signed int)A < (signed int)B){
				*ALUresult = 1;
			}
			else{
				*ALUresult = 0;
				*Zero = 1;
			}
			break;
		case 3:
			//if A < B Z = 1, else Z = 0 (A and B are unsigned int)
			if ( A < B)
				*ALUresult = 1;
			else{
				*ALUresult = 0;
				*Zero = 1;
			}
			break;
		case 4:
			//Z= A AND B
			*ALUresult = A & B;
			break;
		case 5:
			//Z = A OR B
			*ALUresult = A | B;
			break;
		case 6:
			//shift left B by 16 bits
			*ALUresult = B << 4;
			break;
		case 7:
			//Z = NOT A
			*ALUresult = ~A;
			break;
		default:
			//FAIL!!!!!!
			break;
	}
}

/* instruction fetch */
/* 10 Points */
int instruction_fetch(unsigned PC,unsigned *Mem,unsigned *instruction)
{
	*instruction = Mem[(PC >> 2)];

	if(*instruction == 0x0){
		return 1;
	}
	else return 0;
}


/* instruction partition */
/* 10 Points */
void instruction_partition(unsigned instruction, unsigned *op, unsigned *r1,unsigned *r2, unsigned *r3, unsigned *funct, unsigned *offset, unsigned *jsec)
{
	//all partitions are in same format as instruction (hex)
	*op = (instruction >> 26) & 0x3f;	// instruction [31-26]
	*r1 = (instruction >> 21) & 0x1f;	// instruction [25-21]
	*r2 = (instruction >> 16) & 0x1f;	// instruction [20-16]
	*r3 = (instruction >> 11) & 0x1f;	// instruction [15-11]
	*funct = (instruction >> 0) & 0x3f ;	// instruction [5-0]
	*offset = (instruction >> 0) & 0xffff;	// instruction [15-0]
	*jsec = (instruction >> 0) & 0x3ffffff;	// instruction [25-0]
	printf("instruction: %x \n", instruction);
	printf("op: %x \n", *op);	// instruction [31-26]
	printf("r1: %x \n", *r1);	// instruction [25-21]
	printf("r2: %x \n", *r2);	// instruction [20-16]
	printf("r3: %x \n", *r3);	// instruction [15-11]
	printf("funct: %x \n", *funct);	// instruction [5-0]
	printf("offset: %x \n", *offset);	// instruction [15-0]
	printf("jsec: %x \n\n", *jsec);	// instruction [25-0]
}



/* instruction decode */
/* 15 Points */
/*
 * typedef struct
{
	char RegDst;
	char Jump;
	char Branch;
	char MemRead;
	char MemtoReg;
	char ALUOp;
	char MemWrite;
	char ALUSrc;
	char RegWrite;
}struct_controls;
 */
int instruction_decode(unsigned op,struct_controls *controls)
{
	if(op == 0x00){//R-Type
		controls->RegDst = 1;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 0;
		controls->ALUOp = 2;
		controls->MemWrite = 0;
		controls->ALUSrc = 0;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x08){//addi
		controls->RegDst = 0;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 0;
		controls->ALUOp = 0;
		controls->MemWrite = 0;
		controls->ALUSrc = 1;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x0f){//lui
		controls->RegDst = 0;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 1;
		controls->ALUOp = 10;
		controls->MemWrite = 0;
		controls->ALUSrc = 1;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x0a){//slti
		controls->RegDst = 0;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 0;
		controls->ALUOp = 2;
		controls->MemWrite = 0;
		controls->ALUSrc = 1;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x0b){//sltiu
		controls->RegDst = 0;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 0;
		controls->ALUOp = 3;
		controls->MemWrite = 0;
		controls->ALUSrc = 1;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x23){//load
		controls->RegDst = 0;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 1;
		controls->ALUOp = 0;
		controls->MemWrite = 0;
		controls->ALUSrc = 1;
		controls->RegWrite = 1;
		return 0;
	}
	else if(op == 0x2b){//store
		controls->RegDst = 2;
		controls->Jump = 0;
		controls->Branch = 0;
		controls->MemRead = 0;
		controls->MemtoReg = 2;
		controls->ALUOp = 0;
		controls->MemWrite = 1;
		controls->ALUSrc = 1;
		controls->RegWrite = 0;
		return 0;
	}
	else if(op == 0x04){//beq
		controls->RegDst = 2;
		controls->Jump = 0;
		controls->Branch = 1;
		controls->MemRead = 0;
		controls->MemtoReg = 2;
		controls->ALUOp = 1;
		controls->MemWrite = 0;
		controls->ALUSrc = 0;
		controls->RegWrite = 0;
		return 0;
	}
	else if(op == 0x02){//Jump
		controls->RegDst = 2;
		controls->Jump = 1;
		controls->Branch = 0;
		controls->MemRead = 1;
		controls->MemtoReg = 2;
		controls->ALUOp = 0;
		controls->MemWrite = 0;
		controls->ALUSrc = 2;
		controls->RegWrite = 0;
		return 0;
	}
	return 1;
}

/* Read Register */
/* 5 Points */
void read_register(unsigned r1,unsigned r2,unsigned *Reg,unsigned *data1,unsigned *data2)
{
	*data1 = Reg[r1];
	*data2 = Reg[r2];
}


/* Sign Extend */
/* 10 Points */
void sign_extend(unsigned offset,unsigned *extended_value)
{
	if((offset >> 15) == 0)
		*extended_value = 0x0000ffff & offset;
	else if((offset >> 15) == 1){
		//printf("%x", *extended_value);
		*extended_value = (0xffff0000 | offset);
		//printf("%x", *extended_value);
	}
}

/* ALU operations */
/* 10 Points */
int ALU_operations(unsigned data1,unsigned data2,unsigned extended_value,unsigned funct,char ALUOp,char ALUSrc,unsigned *ALUresult,char *Zero)
{
	printf("src: %i op: %i funct: %x\n", ALUSrc, ALUOp, funct);
	if(ALUSrc == 0){//The second ALU operand comes from the second register file output (Read data 2)
		if(ALUOp == 2){
			if(funct == 0x20){//add
				ALU(data1,data2,0, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x22){//sub
				ALU(data1,data2,1, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x2a){//slt
				ALU(data1,data2,2, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x2b){//sltu
				ALU(data1,data2,3, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x28){//AND
				ALU(data1,data2,4, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x29){//OR
				ALU(data1,data2,5, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x00){//sll
				ALU(data1,data2,6, ALUresult, Zero);
				return 0;
			}
			else if(funct == 0x27){//nor
				ALU(data1,data2,7, ALUresult, Zero);
				return 0;
			}
		}
		if(ALUOp == 1){//BEQ
			ALU(data1, data2, 1, ALUresult, Zero);
			return 0;
		}
		return 1;
	}
	else if(ALUSrc == 1){//The second ALU operand is the sign-extended, lower16 bits of the instruction.
		if(ALUOp >= 10){
			return 0;
		}
		ALU(data1,extended_value, ALUOp, ALUresult, Zero);
		return 0;
	}
	else if(ALUSrc == 2){
		//jump, do nothing
		return 0;
	}
	else{
		return 1;
	}
	return 1;
}

/* Read / Write Memory */
/* 10 Points */
int rw_memory(unsigned ALUresult,unsigned data2,char MemWrite,char MemRead,unsigned *memdata,unsigned *Mem)
{
	if(MemWrite == 1 && MemRead == 0){
		Mem[ALUresult >> 2] = data2;
		return 0;
	}
	else if(MemRead == 1 && MemWrite == 0){
		*memdata = Mem[ALUresult >> 2];
		return 0;
	}
	else if(MemRead == 0 && MemWrite == 0) return 0;
	else{
		return 1;
	}
	return 1;
}


/* Write Register */
/* 10 Points */
void write_register(unsigned r2,unsigned r3,unsigned memdata,unsigned ALUresult,char RegWrite,char RegDst,char MemtoReg,unsigned *Reg)
{
	if(RegWrite == 1){
		if(RegDst == 0 && MemtoReg == 0){
			Reg[r2] = ALUresult;
		}
		else if(RegDst == 0 && MemtoReg == 1){
			Reg[r2] = memdata;
		}
		else if(RegDst == 1 && MemtoReg == 0){
			Reg[r3] = ALUresult;
		}
		else if(RegDst == 1 && MemtoReg == 1){
			Reg[r3] = memdata;
		}
	}
}

/* PC update */
/* 10 Points */
void PC_update(unsigned jsec,unsigned extended_value,char Branch,char Jump,char Zero,unsigned *PC)
{
	//printf("Jump: %i    Branch: %i Zero: %i PC: %x\n", Jump, Branch, Zero, *PC);
	if(Jump == 0 && Branch == 0){
		printf("one");
		*PC = *PC + 4;
	}
	else if(Jump == 1 && Branch == 0){
		printf(":(");
		*PC = (jsec << 2) + ((*PC + 4) & 0xf0000000);
	}
	else if(Jump == 0 && Branch == 1 && Zero == 1){
		printf("ughhhhhh");
		*PC = (extended_value << 2) + (*PC & 0xf0000000);
		printf("%x", *PC);
	}
	else printf("blah blah");
}

